package com.lkd.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.OrderCheck;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.dao.OrderDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.OrderEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.service.OrderService;
import com.lkd.util.OrderEntityBuilder;
import com.lkd.vo.*;
import com.lkd.wxpay.WxPayDTO;
import com.lkd.wxpay.WxPaySDKUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private RestHighLevelClient esClient;

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private WxPaySDKUtil wxPaySDKUtil;

    @Autowired
    private MqttProducer mqttProducer;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo,orderNo);
        return this.getOne(qw);
    }


    @Override
    public Pager<OrderVO> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, String startDate, String endDate) {
        return Pager.buildEmpty();
    }


    @Override
    public List<Long> getTop10Sku(Integer businessId) {

        SearchRequest searchRequest=new SearchRequest("order");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //查询条件：最近三个月

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("update_time");
        rangeQueryBuilder.gte( LocalDateTime.now().plusMonths(-3).format(  DateTimeFormatter.ISO_DATE_TIME )  );
        rangeQueryBuilder.lte( LocalDateTime.now().format(  DateTimeFormatter.ISO_DATE_TIME )  );

        BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
        boolQueryBuilder.must( rangeQueryBuilder );

        boolQueryBuilder.must( QueryBuilders.termQuery("business_id",businessId) );
        sourceBuilder.query(boolQueryBuilder);

        AggregationBuilder orderAgg = AggregationBuilders.terms("sku").field("sku_id")
                .subAggregation(AggregationBuilders.count("count").field("sku_id"))
                .order(BucketOrder.aggregation("count", false))
                .size(10);

        sourceBuilder.aggregation(orderAgg);
        searchRequest.source(sourceBuilder);

        try {
            SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            if(aggregations==null ) return  Lists.newArrayList();

            var term = (Terms)aggregations.get("sku");
            var buckets = term.getBuckets();

            return buckets.stream().map(  b->   Long.valueOf( b.getKey().toString() ) ).collect(Collectors.toList());

        } catch (IOException e) {
            e.printStackTrace();
            return Lists.newArrayList();
        }

    }

    @Override
    public String weixinPay(PayVO payVO) {
        OrderEntity order = createOrder(payVO);

        WxPayDTO wxPayDTO = new WxPayDTO();
        wxPayDTO.setOpenid(payVO.getOpenId());
        wxPayDTO.setOutTradeNo(order.getOrderNo());
        wxPayDTO.setExpireSecond(600);
        wxPayDTO.setTotalFee(order.getAmount());
        wxPayDTO.setBody(order.getSkuName());
        return wxPaySDKUtil.requestPay(wxPayDTO);
    }

    @Override
    public void vendOut(String result) {
        //更新订单状态
        OrderEntity orderEntity = getByOrderNo(result);
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_PAYED);
        orderEntity.setStatus(VMSystem.ORDER_STATUS_PAYED);
        updateById(orderEntity);
        //封装消息实体类
        VendoutContract vendoutContract = new VendoutContract();
        VendoutData vendoutData = new VendoutData();
        vendoutData.setOrderNo(result);
        vendoutData.setSkuId(orderEntity.getSkuId());
        vendoutContract.setInnerCode(orderEntity.getInnerCode());
        vendoutContract.setVendoutData(vendoutData);
        String msg = JSON.toJSONString(vendoutContract);
        mqttProducer.send(TopicConfig.VMS_VENDOUT_TOPIC,msg);
    }

    public OrderEntity createOrder(PayVO payVO) {
        VmVO vmVO = vmService.getVMInfo(payVO.getInnerCode());
        SkuVO sku = vmService.getSku(payVO.getSkuId());
        PartnerVO partner = userService.getPartner(vmVO.getOwnerId());
//        OrderEntity orderEntity = getOrderEntity(payVO, vmVO, sku, partner);
        RLock lock = redissonClient.getLock(String.format(payVO.getInnerCode()));
        try {
            if (!lock.tryLock(0L,2L, TimeUnit.MINUTES)) {
                throw new LogicException("设备出货中，请稍后");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //构件者模式封装实体类
        OrderEntity orderEntity = new OrderEntityBuilder(payVO, vmVO, sku, partner)
                .buildVM()
                .buildSku()
                .buildPayVO()
                .buildPartner()
                .buildOrderDetile()
                .builder();

        this.save(orderEntity);
        OrderCheck orderCheck = new OrderCheck();
        orderCheck.setOrderNo(orderEntity.getOrderNo());
        String msg = JSON.toJSONString(orderCheck);
        mqttProducer.send("$delayed/600/"+TopicConfig.ORDER_CHECK_TOPIC,msg);
        return orderEntity;
    }

    private OrderEntity getOrderEntity(PayVO payVO, VmVO vmVO, SkuVO sku, PartnerVO partner) {
        OrderEntity orderEntity = new OrderEntity();
        String orderNo = payVO.getInnerCode()+System.nanoTime()+"";
        orderEntity.setOrderNo(orderNo);
        orderEntity.setInnerCode(payVO.getInnerCode());
        //封装设备相关信息
        BeanUtils.copyProperties(vmVO,orderEntity);
        orderEntity.setAddr(vmVO.getNodeAddr());
        //封装商品相关信息
        orderEntity.setSkuId(Long.valueOf(payVO.getSkuId()));
        orderEntity.setSkuName(sku.getSkuName());
        orderEntity.setClassId(sku.getClassId());
        //封装订单相关信息
        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE);
        orderEntity.setPrice(sku.getPrice());
        orderEntity.setAmount(sku.getRealPrice());

        BigDecimal bg = new BigDecimal(orderEntity.getAmount());
        int bill = bg.multiply(new BigDecimal(partner.getRatio()).divide(new BigDecimal(100), 0, RoundingMode.HALF_UP)).intValue();
        orderEntity.setBill(bill);
        orderEntity.setPayType("2");
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY);
        orderEntity.setOpenId(payVO.getOpenId());
        return orderEntity;
    }

}
